/*++

Copyright (c) 1992  NCR Corporation

Module Name:

    ncrstop.c

Abstract:

    Provides the interface to the firmware for x86.

Author:

    Richard R. Barton (o-richb) 04-Feb-1992

Revision History:

--*/
#include "halp.h"
#include "ncr.h"
#include "ncrcatp.h"

/*
 *  Some plagarism from ixreboot.c
 */

VOID  HalpVideoReboot(VOID);

//
// Defines to let us diddle the CMOS clock and the keyboard
//

#define CMOS_CTRL   (PUCHAR )0x70
#define CMOS_DATA   (PUCHAR )0x71


// below 2 defines are for 3450/3550 machines

#define SHUT_DOWN   0x8F
#define SHUT5       0x05

#define RESETCMD    0xfe
#define KEYBPORT    (PUCHAR )0x64

extern  ULONG   NCRPlatform;

extern BOOLEAN NCRPowerOffSystem;

//
// Private function prototypes
//

VOID
HalpReboot (
    VOID
    );


VOID
HalReturnToFirmware(
    IN FIRMWARE_ENTRY Routine
    )

/*++

Routine Description:

    Returns control to the firmware routine specified.

    BUGBUG  This can probably do useful things (like rebooting) for some
            values of Routine.

Arguments:

    Routine - Supplies a value indicating which firmware routine to invoke.

Return Value:

    Does not return.

--*/

{
    switch (Routine) {
        case HalPowerDownRoutine:
            if (NCRPlatform != NCR3360) {
                HalpCatPowerOffSystem();
            }

        case HalHaltRoutine:
        case HalRestartRoutine:
        case HalRebootRoutine:

            //
            // Never returns
            //

            if ((NCRPlatform != NCR3360) && (NCRPowerOffSystem == TRUE)) {
                HalpCatPowerOffSystem();
            } else {
                HalpVideoReboot();
                HalpReboot();
            }
            break;
        default:
            DBGMSG(("HalReturnToFirmware called\n"));
            DbgBreakPoint();
            break;
    }
}


VOID
HalpReboot (
    VOID
    )

/*++

Routine Description:

    This procedure resets the CMOS clock to the standard timer settings
    so the bios will work, and then issues a reset command to the keyboard
    to cause a warm boot.

    It is very machine dependent, this implementation is intended for
    PC-AT like machines.

    This code copied from the "old debugger" sources.

    N.B.

        Will NOT return.

--*/

{
    UCHAR   Scratch;
    PUSHORT   Magic;

    //
    // By sticking 0x1234 at physical location 0x472, we can bypass the
    // memory check after a reboot.
    //

    Magic = HalpMapPhysicalMemory(0, 1);
    Magic[0x472 / sizeof(USHORT)] = 0x1234;

    //
    // Turn off interrupts
    //

    HalpAcquireCmosSpinLock();

    _asm {
        cli
    }

    //
    // Reset the cmos clock to a standard value
    // (We are setting the periodic interrupt control on the MC147818)
    //

    //
    // Disable periodic interrupt
    //

    WRITE_PORT_UCHAR(CMOS_CTRL, 0x0b);      // Set up for control reg B.
    KeStallExecutionProcessor(1);

    Scratch = READ_PORT_UCHAR(CMOS_DATA);
    KeStallExecutionProcessor(1);

    Scratch &= 0xbf;                        // Clear periodic interrupt enable

    WRITE_PORT_UCHAR(CMOS_DATA, Scratch);
    KeStallExecutionProcessor(1);

    //
    // Set "standard" divider rate
    //

    WRITE_PORT_UCHAR(CMOS_CTRL, 0x0a);      // Set up for control reg A.
    KeStallExecutionProcessor(1);

    Scratch = READ_PORT_UCHAR(CMOS_DATA);
    KeStallExecutionProcessor(1);

    Scratch &= 0xf0;                        // Clear rate setting
    Scratch |= 6;                           // Set default rate and divider

    WRITE_PORT_UCHAR(CMOS_DATA, Scratch);
    KeStallExecutionProcessor(1);

    //
    // Set a "neutral" cmos address to prevent weirdness
    // (Why is this needed? Source this was copied from doesn't say)
    //

    WRITE_PORT_UCHAR(CMOS_CTRL, 0x15);
    KeStallExecutionProcessor(1);

    //
    // for 3450/3550 machines - Set shutdown flag to reset
    //

    if ((NCRPlatform == NCR3450) || (NCRPlatform == NCR3550)) {
        WRITE_PORT_UCHAR(CMOS_CTRL, SHUT_DOWN);
        WRITE_PORT_UCHAR(CMOS_DATA, SHUT5);
    }

    //
    // Send the reset command to the keyboard controller
    //

    WRITE_PORT_UCHAR(KEYBPORT, RESETCMD);

    _asm {
        hlt
    }
}
